/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*-
   this file is part of rcssserver3D
   Fri May 9 2003
   Copyright (C) 2003 Koblenz University
   $Id$

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// -*- C++ -*- generated by wxGlade 0.4 on Wed Jan  4 20:26:10 2006
#include "mainframe.h"
#include "sparkglcanvas.h"
#include "main.h"
#include "propertyframe.h"
#include "kinematicframe.h"
#include "agentframe.h"
#include "sparkedit.h"
#include "platform.h"

#include <wx/filename.h>
#include <wx/mimetype.h>
#include <wx/filedlg.h>
#include <wx/clipbrd.h>

#include <res/xpm_play.xpm>
#include <res/xpm_step.xpm>
#include <res/xpm_pause.xpm>
#include <res/xpm_new.xpm>
#include <res/xpm_open.xpm>
#include <res/xpm_save.xpm>
#include <res/xpm_cut.xpm>
#include <res/xpm_copy.xpm>
#include <res/xpm_paste.xpm>
#include <res/xpm_undo.xpm>
#include <res/xpm_redo.xpm>
#include <res/xpm_reload.xpm>
#include <res/xpm_agent.xpm>
#include "sparkcontextevent.h"

#include "aboutDlg.h"

//! wxWidgets and zeitgeist both use a 'DECLARE_CLASS' macro
#undef DECLARE_CLASS

#include <zeitgeist/logserver/logserver.h>
#include <zeitgeist/scriptserver/scriptserver.h>
#include <oxygen/simulationserver/simulationserver.h>
#include <oxygen/sceneserver/sceneserver.h>
#include <oxygen/sceneserver/fpscontroller.h>
#include <kerosin/inputserver/inputcontrol.h>
#include "simspark.h"
#include "sparkcontext.h"

#define TI_LOG 1
#define TI_FPS 2

BEGIN_EVENT_TABLE(mainframe, wxFrame)
    EVT_MENU(wxID_EXIT, mainframe::OnExit)
    EVT_CLOSE(mainframe::OnClose)

    EVT_UPDATE_UI(ID_SIM_START, mainframe::OnUpdateStartSimulation)
    EVT_MENU(ID_SIM_START, mainframe::OnStartSimulation)
    EVT_MENU(ID_SIM_STEP, mainframe::OnStepSimulation)

    EVT_UPDATE_UI(ID_SIM_PAUSE, mainframe::OnUpdatePauseSimulation)
    EVT_MENU(ID_SIM_PAUSE, mainframe::OnPauseSimulation)

    EVT_MENU(ID_VIEW_LOG, mainframe::OnViewLog)
    EVT_UPDATE_UI(ID_VIEW_LOG, mainframe::OnUpdateViewLog)

    EVT_MENU(ID_VIEW_TREE, mainframe::OnViewTree)
    EVT_UPDATE_UI(ID_VIEW_TREE, mainframe::OnUpdateViewTree)

    EVT_MENU(ID_FILE_NEW, mainframe::OnFileNew)

    EVT_MENU(ID_FILE_OPEN, mainframe::OnFileOpen)
    EVT_UPDATE_UI(ID_FILE_OPEN, mainframe::OnUpdateFileOpen)

    EVT_MENU(ID_FILE_SAVE, mainframe::OnFileSave)
    EVT_MENU(ID_FILE_SAVEAS, mainframe::OnFileSaveAs)
    EVT_UPDATE_UI(ID_FILE_SAVE, mainframe::OnUpdateFileSave)

    EVT_MENU(ID_AGENT_OPEN, mainframe::OnAgentOpen)

    EVT_MENU(ID_FILE_RELOAD, mainframe::OnFileReload)
    EVT_UPDATE_UI(ID_FILE_RELOAD, mainframe::OnUpdateFileReload)

    EVT_MENU(ID_HELP_ABOUT, mainframe::OnHelpAbout)

    EVT_MENU(ID_CONTEXT_PROPERTIES, mainframe::OnContextProperties)
    EVT_MENU(ID_CONTEXT_EDIT_SOURCE, mainframe::OnContextEditSource)
    EVT_MENU(ID_CONTEXT_COPY_FILENAME, mainframe::OnContextCopyFileName)
    EVT_MENU(ID_CONTEXT_COPY_PATH, mainframe::OnContextCopyPath)
    EVT_MENU(ID_CONTEXT_KINEMATIC, mainframe::OnContextKinematic)

    EVT_BUTTON(ID_LOG_CLEAR, mainframe::OnLogClear)
    EVT_BUTTON(ID_LOG_COPY, mainframe::OnLogCopy)
    EVT_CHECKBOX(ID_LOG_CHANNEL_DEBUG, mainframe::OnLogChannel)
    EVT_CHECKBOX(ID_LOG_CHANNEL_NORMAL, mainframe::OnLogChannel)
    EVT_CHECKBOX(ID_LOG_CHANNEL_WARNING, mainframe::OnLogChannel)
    EVT_CHECKBOX(ID_LOG_CHANNEL_ERROR, mainframe::OnLogChannel)

    EVT_TIMER(TI_LOG, mainframe::OnLogTimer)
    EVT_TIMER(TI_FPS, mainframe::OnFPSTimer)

    EVT_TREE_ITEM_EXPANDING(1, mainframe::OnTreeItemExpanding)
    EVT_TREE_SEL_CHANGED(1, mainframe::OnTreeSelChanged)
    EVT_TREE_ITEM_ACTIVATED(1, mainframe::OnTreeItemActivated)
    EVT_TREE_ITEM_RIGHT_CLICK(1, mainframe::OnTreeItemRightClick)

    EVT_FLATNOTEBOOK_PAGE_CLOSING(wxID_ANY, mainframe::OnTabClosing)
    EVT_FLATNOTEBOOK_PAGE_CHANGED(wxID_ANY, mainframe::OnTabChanged)
    EVT_FLATNOTEBOOK_CONTEXT_MENU(wxID_ANY, mainframe::OnTabContextMenu)

    EVT_MENU(ID_TAB_CLOSE, mainframe::OnTabClose)
    EVT_MENU(ID_TAB_CLOSE_OTHER, mainframe::OnTabCloseOther)
    EVT_MENU(ID_TAB_STARTSCRIPT, mainframe::OnTabStartScript)

    EVT_SCI_SAVEPOINTREACHED(wxID_ANY, mainframe::OnEditSavePointReached)
    EVT_SCI_SAVEPOINTLEFT(wxID_ANY, mainframe::OnEditSavePointLeft)

    EVT_MENU(wxID_UNDO, mainframe::OnEditCommand)
    EVT_MENU(wxID_REDO, mainframe::OnEditCommand)
    EVT_MENU(wxID_CUT,  mainframe::OnEditCommand)
    EVT_MENU(wxID_COPY, mainframe::OnEditCommand)
    EVT_MENU(wxID_PASTE, mainframe::OnEditCommand)
    EVT_MENU(wxID_SELECTALL, mainframe::OnEditCommand)

    EVT_UPDATE_UI(wxID_UNDO, mainframe::OnUpdateEditCommand)
    EVT_UPDATE_UI(wxID_REDO, mainframe::OnUpdateEditCommand)
    EVT_UPDATE_UI(wxID_CUT,  mainframe::OnUpdateEditCommand)
    EVT_UPDATE_UI(wxID_COPY, mainframe::OnUpdateEditCommand)
    EVT_UPDATE_UI(wxID_PASTE, mainframe::OnUpdateEditCommand)
    EVT_UPDATE_UI(wxID_SELECTALL, mainframe::OnUpdateEditCommand)

    EVT_SPARK_CONTEXT(ID_SPARK_CONTEXT, mainframe::OnSparkContext)
END_EVENT_TABLE()

using namespace boost;
using namespace zeitgeist;
using namespace kerosin;
using namespace oxygen;

mainframe::mainframe(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long /*style*/):
    wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE),
    mTimer(this,TI_LOG), mFPSTimer(this,TI_FPS)
{
    mToolBar = 0;

    // begin wxGlade: mainframe::mainframe
    mCtrVertSplitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER);
    mRightPane = new wxPanel(mCtrVertSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL);
    mCtrHorSplitter = new wxSplitterWindow(mRightPane, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER);
    mBottomPane = new wxPanel(mCtrHorSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL);
    mTopPane = new wxPanel(mCtrHorSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL);
    mLeftPane = new wxPanel(mCtrVertSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL);
    window_1 = new wxSplitterWindow(mLeftPane, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3DBORDER|wxSP_BORDER);
    window_1_pane_2 = new wxPanel(window_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL);
    window_1_pane_1 = new wxPanel(window_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL);
    MainFrame_menubar = new wxMenuBar();
    SetMenuBar(MainFrame_menubar);
    wxMenu* wxglade_tmp_menu_1 = new wxMenu();
    wxglade_tmp_menu_1->Append(ID_FILE_NEW, wxT("&New"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_1->Append(ID_FILE_OPEN, wxT("&Open\tctrl+o"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_1->Append(ID_FILE_SAVE, wxT("&Save\tctrl+s"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_1->Append(ID_FILE_SAVEAS, wxT("Save as..."), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_1->Append(ID_FILE_RELOAD, wxT("&Reload\tctrl+r"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_1->AppendSeparator();
    wxglade_tmp_menu_1->Append(ID_AGENT_OPEN, wxT("Start &Agent"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_1->Append(wxID_EXIT, wxT("&Exit"), wxEmptyString, wxITEM_NORMAL);
    MainFrame_menubar->Append(wxglade_tmp_menu_1, wxT("&File"));
    wxMenu* wxglade_tmp_menu_2 = new wxMenu();
    wxglade_tmp_menu_2->Append(wxID_UNDO, wxT("&Undo"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_2->Append(wxID_REDO, wxT("&Redo"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_2->AppendSeparator();
    wxglade_tmp_menu_2->Append(wxID_CUT, wxT("Cu&t"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_2->Append(wxID_COPY, wxT("&Copy"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_2->Append(wxID_PASTE, wxT("&Paste"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_2->AppendSeparator();
    wxglade_tmp_menu_2->Append(wxID_SELECTALL, wxT("Select &All"), wxEmptyString, wxITEM_NORMAL);
    MainFrame_menubar->Append(wxglade_tmp_menu_2, wxT("&Edit"));
    wxMenu* wxglade_tmp_menu_3 = new wxMenu();
    wxglade_tmp_menu_3->Append(ID_VIEW_LOG, wxT("message &log"), wxT("Show/Hide message log"), wxITEM_CHECK);
    wxglade_tmp_menu_3->Append(ID_VIEW_TREE, wxT("object &tree"), wxT("Show/Hide object tree"), wxITEM_CHECK);
    MainFrame_menubar->Append(wxglade_tmp_menu_3, wxT("&View"));
    wxMenu* wxglade_tmp_menu_4 = new wxMenu();
    wxglade_tmp_menu_4->Append(ID_SIM_STEP, wxT("St&ep"), wxEmptyString, wxITEM_NORMAL);
    wxglade_tmp_menu_4->Append(ID_SIM_START, wxT("&Start"), wxT("Start simulation"), wxITEM_NORMAL);
    wxglade_tmp_menu_4->Append(ID_SIM_PAUSE, wxT("&Pause"), wxT("Pause Simulation"), wxITEM_NORMAL);
    MainFrame_menubar->Append(wxglade_tmp_menu_4, wxT("&Simulation"));
    wxMenu* wxglade_tmp_menu_5 = new wxMenu();
    wxglade_tmp_menu_5->Append(ID_HELP_ABOUT, wxT("&About"), wxEmptyString, wxITEM_NORMAL);
    MainFrame_menubar->Append(wxglade_tmp_menu_5, wxT("&Help"));
    MainFrame_statusbar = CreateStatusBar(1, 0);
    mCtrPropList = new wxListCtrl(window_1_pane_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSIMPLE_BORDER);
    mCtrTree = new wxTreeCtrl(window_1_pane_2, 1, wxDefaultPosition, wxDefaultSize, wxTR_HAS_BUTTONS|wxTR_NO_LINES|wxTR_LINES_AT_ROOT|wxTR_DEFAULT_STYLE|wxSIMPLE_BORDER);
    mCtrNotebook = new wxFlatNotebook(mTopPane, wxID_ANY);
    mCtrLog = new wxTextCtrl(mBottomPane, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL|wxNO_BORDER);
    button_2 = new wxButton(mBottomPane, ID_LOG_CLEAR, wxT("Clear"));
    button_3 = new wxButton(mBottomPane, ID_LOG_COPY, wxT("Copy"));
    static_line_1 = new wxStaticLine(mBottomPane, wxID_ANY);
    label_2 = new wxStaticText(mBottomPane, wxID_ANY, wxT("Log Channels"));
    mCtrLogChannelDebug = new wxCheckBox(mBottomPane, ID_LOG_CHANNEL_DEBUG, wxT("Debug"));
    mCtrLogChannelNormal = new wxCheckBox(mBottomPane, ID_LOG_CHANNEL_NORMAL, wxT("Normal"));
    mCtrLogChannelWarning = new wxCheckBox(mBottomPane, ID_LOG_CHANNEL_WARNING, wxT("Warning"));
    mCtrLogChannelError = new wxCheckBox(mBottomPane, ID_LOG_CHANNEL_ERROR, wxT("Error"));

    set_properties();
    do_layout();
    // end wxGlade

    mCtrNotebook->SetWindowStyleFlag
        (mCtrNotebook->GetWindowStyleFlag()
         | wxFNB_DROPDOWN_TABS_LIST
         | wxFNB_X_ON_TAB
         | wxFNB_MOUSE_MIDDLE_CLOSES_TABS
         | wxFNB_VC8
         );

    mCanvas = new SparkGLCanvas(mCtrNotebook, wxID_ANY);
    mCanvas->SetLabel(wxT("simulation window"));
    mCtrNotebook->AddPage(mCanvas, wxT("Canvas"));

    SparkEdit::GetInstance().PrepareImageList(mCtrNotebook);
    mCtrNotebook->SetPageImageIndex(0, SparkEdit::GetInstance().II_SIMSPARK);

    // create toolbar
    mToolBar = CreateToolBar( wxTB_FLAT|wxTB_HORIZONTAL, wxID_ANY );

    mToolBar->AddTool(ID_FILE_NEW, wxT("New"), wxBitmap(xpm_new));
    mToolBar->AddTool(ID_FILE_OPEN, wxT("Open"), wxBitmap(xpm_open));
    mToolBar->AddTool(ID_FILE_RELOAD, wxT("Reload"), wxBitmap(xpm_reload));
    mToolBar->AddTool(ID_FILE_SAVE, wxT("Save"), wxBitmap(xpm_save));
    mToolBar->AddSeparator();
    mToolBar->AddTool(ID_AGENT_OPEN, wxT("Start Agent"), wxBitmap(xpm_agent));
    mToolBar->AddSeparator();
    mToolBar->AddTool(ID_SIM_STEP, wxT("Step"), wxBitmap(xpm_step));
    mToolBar->AddTool(ID_SIM_START, wxT("Start"), wxBitmap(xpm_play));
    mToolBar->AddTool(ID_SIM_PAUSE, wxT("Pause"), wxBitmap(xpm_pause));
    mToolBar->AddSeparator();
    mToolBar->AddTool(wxID_CUT, wxT("Cut"), wxBitmap(xpm_cut));
    mToolBar->AddTool(wxID_COPY, wxT("Copy"), wxBitmap(xpm_copy));
    mToolBar->AddTool(wxID_COPY, wxT("Paste"), wxBitmap(xpm_paste));
    mToolBar->AddSeparator();
    mToolBar->AddTool(wxID_UNDO, wxT("Undo"), wxBitmap(xpm_undo));
    mToolBar->AddTool(wxID_REDO, wxT("Redo"), wxBitmap(xpm_redo));
    mToolBar->AddSeparator();

    mSimState = new wxTextCtrl( mToolBar, wxID_ANY, _T(""), wxDefaultPosition, wxSize(120, -1), wxTE_READONLY );
    mToolBar->AddControl(mSimState);

    mToolBar->Realize();
    SetToolBar(mToolBar);

    //
    // init horizontal splitterwindow (glcanvas / logwnd)
    wxSize szClient = GetClientSize();
    mLastHorSashPosition = std::max<int>(1,szClient.y - LOGWND_DEFAULT_HEIGHT);
    SplitHor();

    // init vertical splitterwindow
    mLastVertSashPosition = std::max<int>(1,TREEWND_DEFAULT_WIDTH);
    SplitVert();

    //
    // init spark tree
    mSparkTree.Init(mCtrTree);
    InitTreeNodeProperties();

    // init log wnd timer
    mTimer.Start(LOGWND_UPDATE_INTERVAL);

    // init spark context
    SparkContext::GetInstance().ResetSelection();
    SparkContext::GetInstance().SetEventHandler(this, ID_SPARK_CONTEXT);

    PrintSimState();
    UpdateTitle();
    UpdateLogChannelState();
}

mainframe::~mainframe()
{
}

void mainframe::UpdateTitle()
{
    wxString title(wxT("SimSpark"));
    if (SparkEdit::GetInstance().GetStartScript() != 0)
    {
        title += wxT(" <") + SparkEdit::GetInstance().GetStartScriptFile() + wxT(">");
    }

    SetTitle(title);
}

void mainframe::SplitHor()
{
    mCtrHorSplitter->SplitHorizontally(mTopPane, mBottomPane);
    mCtrHorSplitter->SetSashPosition(mLastHorSashPosition);
#if wxCHECK_VERSION(2,5,4)
    mCtrHorSplitter->SetSashGravity(1.0);
#endif
}

void mainframe::SplitVert()
{
    mCtrVertSplitter->SplitVertically(mLeftPane, mRightPane);
    mCtrVertSplitter->SetSashPosition(mLastVertSashPosition);
#if wxCHECK_VERSION(2,5,4)
    // only resize right pane when window is resized
    mCtrVertSplitter->SetSashGravity(0.0);
#endif
}

void mainframe::set_properties()
{
    // begin wxGlade: mainframe::set_properties
    SetTitle(wxT("RsgEdit"));
    SetSize(wxSize(1076, 786));
    int MainFrame_statusbar_widths[] = { 0 };
    MainFrame_statusbar->SetStatusWidths(1, MainFrame_statusbar_widths);
    const wxString MainFrame_statusbar_fields[] = {
        wxT("Welcome to RsgEdit")
    };
    for(int i = 0; i < MainFrame_statusbar->GetFieldsCount(); ++i) {
        MainFrame_statusbar->SetStatusText(MainFrame_statusbar_fields[i], i);
    }
    // end wxGlade
}

void mainframe::do_layout()
{
    // begin wxGlade: mainframe::do_layout
    wxBoxSizer* sizer_1 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_2 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_6 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* sizer_11 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_5 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_7 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_8 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer_4 = new wxBoxSizer(wxVERTICAL);
    sizer_4->Add(mCtrPropList, 1, wxEXPAND, 0);
    window_1_pane_1->SetSizer(sizer_4);
    sizer_8->Add(mCtrTree, 1, wxEXPAND, 0);
    window_1_pane_2->SetSizer(sizer_8);
    window_1->SplitHorizontally(window_1_pane_1, window_1_pane_2);
    sizer_7->Add(window_1, 1, wxEXPAND, 0);
    mLeftPane->SetSizer(sizer_7);
    sizer_5->Add(mCtrNotebook, 1, wxEXPAND, 0);
    mTopPane->SetSizer(sizer_5);
    sizer_6->Add(mCtrLog, 1, wxEXPAND, 0);
    sizer_11->Add(button_2, 0, wxALL|wxEXPAND, 5);
    sizer_11->Add(button_3, 0, wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND, 5);
    sizer_11->Add(static_line_1, 0, wxLEFT|wxRIGHT|wxTOP|wxEXPAND, 5);
    sizer_11->Add(label_2, 0, wxALL, 5);
    sizer_11->Add(mCtrLogChannelDebug, 0, wxLEFT|wxRIGHT|wxBOTTOM, 5);
    sizer_11->Add(mCtrLogChannelNormal, 0, wxLEFT|wxRIGHT|wxBOTTOM, 5);
    sizer_11->Add(mCtrLogChannelWarning, 0, wxLEFT|wxRIGHT|wxBOTTOM, 5);
    sizer_11->Add(mCtrLogChannelError, 0, wxLEFT|wxRIGHT|wxBOTTOM, 5);
    sizer_6->Add(sizer_11, 0, 0, 0);
    mBottomPane->SetSizer(sizer_6);
    mCtrHorSplitter->SplitHorizontally(mTopPane, mBottomPane);
    sizer_2->Add(mCtrHorSplitter, 1, wxEXPAND, 0);
    mRightPane->SetSizer(sizer_2);
    mCtrVertSplitter->SplitVertically(mLeftPane, mRightPane);
    sizer_1->Add(mCtrVertSplitter, 1, wxEXPAND, 0);
    SetSizer(sizer_1);
    Layout();
    // end wxGlade
}

ESimState mainframe::GetSimState() const
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (spark.get() == 0)
        {
            assert(false);
            return S_PAUSED;
        }

    return spark->GetSimState();
}

bool mainframe::CanClose()
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (
        (spark.get() != 0) &&
        (spark->GetSimState() == S_RUNNING)
        )
        {
            return false;
        }

    return true;
}


void mainframe::OnClose( wxCloseEvent& event )
{
    if (! CanClose())
        {
            PauseSimulation();
            event.Veto();
            return;
        }

    int n = mCtrNotebook->GetPageCount();

    for (int i=(n-1);i>=0;--i)
    {
        wxWindow* page = mCtrNotebook->GetPage(i);
        if (page == mCanvas)
        {
            continue;
        }

        if (! mCtrNotebook->RemovePage(i))
        {
            event.Veto();
            return;
        }
    }

    // close agentframes
    wxWindowList& children = GetChildren();
    for (
         wxWindowList::Node *node = children.GetFirst();
         node != 0;
         node = node->GetNext()
         )
        {
            agentframe* af = dynamic_cast<agentframe*>(node->GetData());
            if (af == 0)
            {
                continue;
            }

            af->Close();
         }

    event.Skip();
}

void mainframe::OnExit( wxCommandEvent& WXUNUSED(event) )
{
    Close();
}

void mainframe::OnUpdateStartSimulation(wxUpdateUIEvent& event)
{
    event.Enable(GetSimState() == S_PAUSED);
}

void mainframe::OnStartSimulation(wxCommandEvent& /*event*/)
{
    bool singleStep = false;
    StartSimulation(singleStep);
}

void mainframe::OnUpdateStepSimulation(wxUpdateUIEvent& event)
{
    event.Enable(GetSimState() == S_PAUSED);
}

void mainframe::OnStepSimulation(wxCommandEvent& /*event*/)
{
    bool singleStep = true;
    StartSimulation(singleStep);
}

void mainframe::OnUpdatePauseSimulation(wxUpdateUIEvent& event)
{
    event.Enable(GetSimState() == S_RUNNING);
}

void mainframe::OnPauseSimulation(wxCommandEvent& /*event*/)
{
    PauseSimulation();
}

void mainframe::OnLogTimer(wxTimerEvent& /*event*/)
{
    UpdateLogWindow();
}

void mainframe::OnFPSTimer(wxTimerEvent& /*event*/)
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();

    if (
        (spark.get() == 0) ||
        (spark->GetSimState() != S_PAUSED)
        )
        {
            return;
        }

    shared_ptr<InputControl> inputCtr = spark->GetInputControl();
    if (inputCtr.get() == 0)
        {
            return;
        }

    inputCtr->StartCycle();

    shared_ptr<FPSController> fps = inputCtr->GetFPSController();
    if (
        (fps.get() == 0) ||
        (! fps->NeedStaticUpdate())
        )
        {
            // currently no update need, check again after full interval
            mFPSTimer.Start(FPS_UPDATE_INTERVAL);
            return;
        }

    shared_ptr<SimulationServer> sim = spark->GetSimulationServer();
    if (sim.get() == 0)
    {
        return;
    }

    float now = sim->GetTime();
    fps->UpdateStatic(std::max<float>(0, now - mLastFPSUpdate));
    mLastFPSUpdate = now;

    wxClientDC dc(this);

    bool swapBuffers = true;
    mCanvas->Render(dc, swapBuffers);

    // recheck and update as soon as posssible
    mFPSTimer.Start(1);
}

void mainframe::UpdateLogWindow()
{
    if (! SimSpark::HasLogContents())
        {
            return;
        }

    wxString buffer = SimSpark::GetLogBuffer();
    SimSpark::ClearLogBuffer();
    mCtrLog->AppendText(buffer);
    mCtrLog->ShowPosition(mCtrLog->GetLastPosition() + 1);
}

void mainframe::PauseSimulation()
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();

    if (
        (spark.get() == 0) ||
        (spark->GetSimState() != S_RUNNING)
        )
        {
            return;
        }

    shared_ptr<SimulationServer> sim = spark->GetSimulationServer();
    if (sim.get() == 0)
        {
            return;
        }

    sim->Quit();
    spark->SetSimState(S_PAUSED);
    spark->GetLog()->Normal() << "(RsgEdit) pausing simulation\n";
    PrintSimState();
}

void mainframe::ResetSimulation()
{
    PauseSimulation();
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();

    if (
        (spark.get() == 0) ||
        (spark->GetSimState() != S_PAUSED)
        )
        {
            assert(false);
            return;
        }

    spark->ResetSimulation();
    mCanvas->Reset();
    spark->GetLog()->Normal() << "(RsgEdit) resetting simulation\n";
}

void mainframe::AdvanceSimulation(shared_ptr<SimulationServer>& sim, float tDeltaSec)
{
    if (sim.get() == 0)
        {
            assert(false);
            return;
        }

    // go through the next simulation cycle
	sim->SetSimStep(tDeltaSec);
    sim->Cycle();

    // refresh the display
    RefreshProperties();

    wxClientDC dc(this);

    bool swapBuffers = true;
    mCanvas->Render(dc, swapBuffers);
}

void mainframe::InitSimulation(shared_ptr<SimulationServer>& sim)
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (
        (spark.get() == 0) ||
        (sim.get() == 0)
        )
    {
        assert(false);
        return;
    }

    // tell the inputControl node the loaction of our camera
    shared_ptr<InputControl> inputCtr = spark->GetInputControl();
    if (inputCtr.get() != 0)
        {
            inputCtr->SetFPSController(std::string(MAINFRAME_FPS_CONTROLLER.fn_str()));
            inputCtr->SetAdvanceTime(false);
        }

    sim->SetSimStep(SIM_SIMSTEP);
    sim->Init(0,0);

    mLastFPSUpdate = 0.0;
}

void mainframe::DoneSimulation(shared_ptr<SimulationServer>& sim)
{
    if (sim.get() == 0)
        {
            assert(false);
            return;
        }

    sim->Done();

    mLastFPSUpdate = sim->GetTime();
    mFPSTimer.Start(FPS_UPDATE_INTERVAL);
}

void mainframe::StartSimulation(bool singleStep)
{
    mFPSTimer.Stop();
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (
        (spark.get() == 0) ||
        (spark->GetSimState() != S_PAUSED)
        )
        {
            return;
        }

    shared_ptr<SimulationServer> sim = spark->GetSimulationServer();
    if (sim.get() == 0)
        {
            return;
        }

    spark->SetSimState(S_RUNNING);

    InitSimulation(sim);

    spark->GetLog()->Normal() << "(RsgEdit) starting simulation\n";
    wxLongLong tLast = wxGetLocalTimeMillis();

    // force update of ui state
    UpdateWindowUI(wxUPDATE_UI_RECURSE);

    if (singleStep)
        {
            // leave simulation loop after first iteration
            sim->Quit();
        }

    do {
        if (SIM_SIMSTEP > 0.0f)
            {
                AdvanceSimulation(sim, SIM_SIMSTEP);
            } else
            {
                wxLongLong tNow = wxGetLocalTimeMillis();
                float tDeltaSec = (tNow - tLast).ToLong() / 1000.0;
                tLast = tNow;

                AdvanceSimulation(sim, tDeltaSec);
            }

        PrintSimState();

        // pump the wxWidgets message loop
        wxGetApp().Yield();
    } while (! sim->WantsToQuit());

    DoneSimulation(sim);

    spark->SetSimState(S_PAUSED);
    spark->GetLog()->Normal() << "(RsgEdit) simulation paused\n";

    // force update of ui state
    UpdateWindowUI(wxUPDATE_UI_RECURSE);
}

void mainframe::OnTreeItemExpanding(wxTreeEvent& event)
{
    if (! mSparkTree.CreateChildren(event.GetItem()))
        {
            event.Veto();
            return;
        }
}

void mainframe::InitTreeNodeProperties()
{
    mPropList.Init(mSparkTree.GetLeaf(mCtrTree->GetSelection()), mCtrPropList);
    RefreshProperties();
}

void mainframe::OnTreeSelChanged(wxTreeEvent& /*event*/)
{
    InitTreeNodeProperties();
}

void mainframe::OnTreeItemRightClick(wxTreeEvent& event)
{
    shared_ptr<Leaf> leaf = mSparkTree.GetLeaf(event.GetItem()).lock();
    wxMenu* context = SparkContext::GetInstance().GetContextMenu(leaf);

    if (context == 0)
        {
            return;
        }

    PopupMenu(context);
}

void mainframe::OnTreeItemActivated(wxTreeEvent& event)
{
    shared_ptr<Leaf> leaf = mSparkTree.GetLeaf(event.GetItem()).lock();
    if (leaf.get() == 0)
        {
            return;
        }

    SparkContext::GetInstance().SetSelection(leaf);
}

void mainframe::OnContextKinematic(wxCommandEvent& /*event*/)
{
    weak_ptr<Leaf> leaf = SparkContext::GetInstance().GetContextNode();
    if (leaf.expired())
        {
            assert(false);
            return;
        }

    wxString location(leaf.lock()->GetFullPath().c_str(), wxConvUTF8);

    kinematicFrame* frame(new kinematicFrame(this, wxID_ANY, location));
    frame->SetParent(leaf);
    frame->SetTitle(location);
    frame->Show();
}

void mainframe::OnContextProperties(wxCommandEvent& /*event*/)
{
    weak_ptr<Leaf> leaf = SparkContext::GetInstance().GetContextNode();
    if (leaf.expired())
        {
            assert(false);
            return;
        }

    wxString location(leaf.lock()->GetFullPath().c_str(), wxConvUTF8);

    propertyframe* frame(new propertyframe(this, wxID_ANY, location));
    frame->SetLeaf(leaf);
    frame->SetTitle(location);
    frame->Show();
}

void mainframe::OnContextEditSource(wxCommandEvent& /*event*/)
{
    const SceneDict::FileRef* ref = GetContextFileRef();
    if (ref == 0)
    {
        assert(false);
        return;
    }

    EditFile(wxString::FromAscii(ref->fname.c_str()), ref->line);
}

void mainframe::OnContextCopyFileName(wxCommandEvent& /*event*/)
{
    const SceneDict::FileRef* ref = GetContextFileRef();
    if (ref == 0)
    {
        assert(false);
        return;
    }

    wxFileName fn(wxString::FromAscii(ref->fname.c_str()));
    fn.Normalize();
    CopyToClipboard(fn.GetFullPath());
}

void mainframe::OnContextCopyPath(wxCommandEvent& /*event*/)
{
    weak_ptr<Leaf> leaf = SparkContext::GetInstance().GetContextNode();
    if (leaf.expired())
        {
            assert(false);
            return;
        }

    CopyToClipboard(wxString::FromAscii(leaf.lock()->GetFullPath().c_str()));
}

const SceneDict::FileRef* mainframe::GetContextFileRef()
{
    weak_ptr<Leaf> leaf = SparkContext::GetInstance().GetContextNode();
    if (leaf.expired())
        {
            assert(false);
            return 0;
        }

    return SceneDict::GetInstance().Lookup(leaf);
}

void mainframe::CopyToClipboard(const wxString& str) const
{
    wxTheClipboard->Open();
    wxTheClipboard->SetData(new wxTextDataObject(str));
    wxTheClipboard->Close();
}

void mainframe::EditFile(const wxString& fname, int line)
{
#if 0
    wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromMimeType("text/plain");
    if (ft == 0)
        {
            return;
        }

    wxFileName fn(fname);
    fn.Normalize();

    wxString cmd = ft->GetOpenCommand(fn.GetFullPath());
    if (cmd.IsEmpty())
        {
            return;
        }

    wxExecute(cmd);
#endif

    bool select = true;
    wxScintilla* edit = SparkEdit::GetInstance().GetEdit(fname,mCtrNotebook,select);
    if (edit != 0)
        {
            edit->GotoLine(line);
        }

    if (edit == 0)
    {
        return;
    }

    edit->GotoLine(line);
}

void mainframe::RefreshProperties()
{
    mPropList.Refresh();

    wxWindowList& children = GetChildren();
    for (
         wxWindowList::Node *node = children.GetFirst();
         node != 0;
         node = node->GetNext()
         )
        {
            propertyframe* pf = dynamic_cast<propertyframe*>(node->GetData());
            if (pf != 0)
                {
                    pf->RefreshProperties();
                    continue;
                }

            kinematicFrame* kf = dynamic_cast<kinematicFrame*>(node->GetData());
            if (kf != 0)
                {
                    kf->RefreshProperties();
                    continue;
                }
        }
}

void mainframe::UpdateCached()
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (spark.get() == 0)
        {
            return;
        }

    spark->UpdateCached();

    wxWindowList& children = GetChildren();
    for (
         wxWindowList::Node *node = children.GetFirst();
         node != 0;
         node = node->GetNext()
         )
        {
            propertyframe* pf = dynamic_cast<propertyframe*>(node->GetData());
            if (pf != 0)
                {
                    pf->UpdateCached();
                    continue;
                }

            kinematicFrame* kf = dynamic_cast<kinematicFrame*>(node->GetData());
            if (kf != 0)
                {
                    kf->UpdateCached();
                    continue;
                }
        }
}

void mainframe::OnUpdateViewTree(wxUpdateUIEvent& event)
{
    event.Check(mCtrVertSplitter->IsSplit());
}

void mainframe::OnViewTree(wxCommandEvent& /*event*/)
{
    if (mCtrVertSplitter->IsSplit())
        {
            mLastVertSashPosition = mCtrVertSplitter->GetSashPosition();
            mCtrVertSplitter->Unsplit(mLeftPane);
        } else
        {
            SplitVert();
        }
}

void mainframe::OnUpdateViewLog(wxUpdateUIEvent& event)
{
    event.Check(mCtrHorSplitter->IsSplit());
}

void mainframe::OnViewLog(wxCommandEvent& /*event*/)
{
    if (mCtrHorSplitter->IsSplit())
        {
            mLastHorSashPosition = mCtrHorSplitter->GetSashPosition();
            mCtrHorSplitter->Unsplit(mBottomPane);
        } else
        {
            SplitHor();
        }
}

void mainframe::OnUpdateFileOpen(wxUpdateUIEvent& event)
{
    event.Enable(GetSimState() == S_PAUSED);
}

bool mainframe::OpenSimulation(const wxString& fname)
{
    ResetSimulation();
    mSparkTree.Init(mCtrTree);
    InitTreeNodeProperties();

    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (spark.get() == 0)
        {
            assert(false);
            return false;
        }

    spark->GetLog()->Normal() << "(RsgEdit) reading " << fname << "\n";
    if (! spark->GetScriptServer()->Run(std::string(fname.fn_str())))
    {
        return false;
    }

    UpdateCached();

    shared_ptr<SimulationServer> sim = spark->GetSimulationServer();
    if (sim.get() == 0)
        {
            assert(false);
            return false;
        }

    InitSimulation(sim);

    if (SIM_SIMSTEP > 0.0)
        {
            AdvanceSimulation(sim, SIM_SIMSTEP);
        } else
        {
            AdvanceSimulation(sim, 0.01f);
        }

    DoneSimulation(sim);
    PrintSimState();

    return true;
}

void mainframe::SaveModified()
{
    SparkEdit::GetInstance().SaveModified();
    SparkEdit::GetInstance().UpdateTitles(mCtrNotebook);
}

bool mainframe::SaveFileAs(wxScintilla* edit)
{
    if (edit == 0)
    {
        return false;
    }

    wxString message(wxT("Choose a file to save"));
    wxString default_path;
    wxString default_filename;
    wxString default_extension;
    wxString wildcard(wxT("simspark files (*.rsg, *.rb)|*.rb;*.rsg"));
    int flags = wxFD_SAVE | wxFD_OVERWRITE_PROMPT;

    wxString filename = wxFileSelector(message, default_path, default_filename,
                                       default_extension, wildcard,flags);

    if (filename.empty() )
    {
        return false;
    }

    SparkEdit::GetInstance().SaveFileAs(mCtrNotebook, edit, filename);
    return true;
}

void mainframe::OnFileSaveAs(wxCommandEvent& /*event*/)
{
    wxScintilla* edit = SparkEdit::GetCurrentPage(mCtrNotebook);
    if (edit == 0)
    {
        return;
    }

    SaveFileAs(edit);
}

void mainframe::OnUpdateFileSave(wxUpdateUIEvent& event)
{
    event.Enable(SparkEdit::GetCurrentPage(mCtrNotebook) != 0);
}

bool mainframe::SaveFile(wxScintilla* edit)
{
    if (edit == 0)
    {
        return false;
    }

    if (SparkEdit::GetInstance().GetFile(edit).IsEmpty())
        {
            return SaveFileAs(edit);
        }

    SparkEdit::GetInstance().SaveFile(edit);
    return true;
}

void mainframe::OnFileSave(wxCommandEvent& /*event*/)
{
    wxScintilla* edit = SparkEdit::GetCurrentPage(mCtrNotebook);
    if (edit == 0)
    {
        return;
    }

    SaveFile(edit);
}

void mainframe::SelectCanvasTab()
{
    int idx = mCtrNotebook->GetPageIndex(mCanvas);
    if (idx < 0)
    {
        assert(false);
        return;
    }

    mCtrNotebook->SetSelection(idx);
    mCanvas->SetFocus();
}

void mainframe::OnFileNew(wxCommandEvent& /*event*/)
{
    bool select = true;
    SparkEdit::GetInstance().GetEdit(wxString(), mCtrNotebook, select);
}

void mainframe::OnFileOpen(wxCommandEvent& /*event*/)
{
    wxString message(wxT("Choose a file to open"));
    wxString default_filename(wxT(""));
    wxString default_extension(wxT(""));
    wxString wildcard(wxT("simspark files (*.rsg, *.rb)|*.rb;*.rsg"));
    int flags = wxFD_OPEN | wxFD_FILE_MUST_EXIST;

    wxString filename = wxFileSelector(message, Platform::GetDefaultFileOpenPath(),
                                       default_filename, default_extension, wildcard, flags);
    if (filename.empty() )
    {
        return;
    }

    bool select = false;
    wxScintilla* edit = SparkEdit::GetInstance().GetEdit(filename, mCtrNotebook, select);

    if (
        (SparkEdit::GetInstance().GetStartScript() != 0) ||
        (SparkEdit::GetInstance().GetFileType(edit) != SparkEdit::GetInstance().ET_RB)
        )
    {
        SparkEdit::GetInstance().SelectEdit(mCtrNotebook, edit);
        return;
    }

    SelectCanvasTab();
    SparkEdit::GetInstance().SetStartScript(mCtrNotebook, edit);
    UpdateTitle();
    OpenSimulation(filename);
}

void mainframe::OnUpdateFileReload(wxUpdateUIEvent& event)
{
    event.Enable(
                 (GetSimState() == S_PAUSED) &&
                 (SparkEdit::GetInstance().GetStartScript() != 0)
                 );
}

void mainframe::OnFileReload(wxCommandEvent& /*event*/)
{
    wxString simName = SparkEdit::GetInstance().GetStartScriptFile();
    if (simName.IsEmpty())
    {
        return;
    }

    SaveModified();
    SelectCanvasTab();
    OpenSimulation(simName);
}

void mainframe::OnHelpAbout(wxCommandEvent& /*event*/)
{
    aboutDlg dlg(this, wxID_ANY, wxT("About SimSpark"));
    dlg.ShowModal();
}

void mainframe::OnAgentOpen(wxCommandEvent& /*event*/)
{
    wxString message(wxT("Choose an agent executable to run"));
    wxString default_filename(wxT(""));
    wxString default_extension(wxT(""));
    wxString wildcard(wxT("agent executables (*.exe)|*.exe"));

    wxString filename = wxFileSelector(message, Platform::GetDefaultAgentOpenPath(),
                                       default_filename, default_extension,
                                       Platform::GetDefaultAgentOpenWildcard());
    if (filename.empty() )
        {
            return;
        }

    agentframe* frame(new agentframe(this, wxID_ANY, filename));
    frame->SetTitle(wxFileName(filename).GetName());
    frame->Show();
    frame->StartAgent(filename);
}

void mainframe::PrintSimState()
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();

    if (spark.get() == 0)
        {
            mSimState->SetValue(wxString());
            return;
        }

    shared_ptr<SimulationServer> sim = spark->GetSimulationServer();
    if (sim.get() == 0)
        {
            mSimState->SetValue(wxString());
            return;
        }

    wxString state;
    switch (spark->GetSimState())
        {
        default:
            break;

        case S_PAUSED:
            state = wxT("Paused ");
            break;

        case S_RUNNING:
            state = wxT("Running ");
            break;
        }

    float now = sim->GetTime();
    mSimState->SetValue(state + wxString::Format(wxT("t=%.1lf"),now));
}

void mainframe::OnSparkContext(SparkContextEvent& event)
{
    switch (event.GetType())
    {
    default:
        assert(false);
        break;

    case SparkContextEvent::T_SELECTION_CHANGED:
        {
            // redraw gl canvas
            bool swapBuffers = true;
            wxClientDC dc(this);
            mCanvas->Render(dc, swapBuffers);

            weak_ptr<Leaf> leaf = SparkContext::GetInstance().GetSelection();
            if (mSparkTree.SelectLeaf(leaf))
            {
                mSparkTree.MarkItem(mCtrTree->GetSelection());
            } else
            {
                mSparkTree.MarkItem(wxTreeItemId());
            }

            break;
        }
    }
}

void mainframe::OnLogClear(wxCommandEvent& /*event*/)
{
    mCtrLog->SetValue(wxString());
}

void mainframe::OnLogCopy(wxCommandEvent& /*event*/)
{
    long from, to;
    mCtrLog->GetSelection(&from,&to);
    mCtrLog->SetSelection(-1,-1);
    mCtrLog->Copy();
    mCtrLog->SetSelection(from,to);
}

void mainframe::UpdateLogChannelState()
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (spark.get() == 0)
        {
            assert(false);
            return;
        }

    unsigned int mask = spark->GetLogPriority();

    mCtrLogChannelDebug->SetValue((mask & LogServer::eDebug) != 0);
    mCtrLogChannelNormal->SetValue((mask & LogServer::eNormal) != 0);
    mCtrLogChannelWarning->SetValue((mask & LogServer::eWarning) != 0);
    mCtrLogChannelError->SetValue((mask & LogServer::eError) != 0);
}

void mainframe::OnLogChannel(wxCommandEvent& /*event*/)
{
    shared_ptr<SimSpark> spark = wxGetApp().GetSpark();
    if (spark.get() == 0)
        {
            assert(false);
            return;
        }

    unsigned int mask = LogServer::eNone;

    mask += mCtrLogChannelDebug->GetValue() ? static_cast<int>(LogServer::eDebug) : 0;
    mask += mCtrLogChannelNormal->GetValue() ? static_cast<int>(LogServer::eNormal) : 0;
    mask += mCtrLogChannelWarning->GetValue() ? static_cast<int>(LogServer::eWarning) : 0;
    mask += mCtrLogChannelError->GetValue() ? static_cast<int>(LogServer::eError) : 0;

    spark->SetLogPriority(mask);
}

void mainframe::OnTabClosing(wxFlatNotebookEvent& event)
{
    wxWindow* page = mCtrNotebook->GetPage(event.GetSelection());
    if (page == mCanvas)
    {
        event.Veto();
        return;
    }

    wxScintilla* edit = dynamic_cast<wxScintilla*>(page);
    if (edit == 0)
    {
        event.Skip();
        return;
    }

    if (edit->GetModify())
        {
            int res = wxMessageBox(wxT("The file '")+SparkEdit::GetInstance().GetTitle(edit)+wxT("' is modified.\n")
                                   wxT("Do you want to save your changes?"),
                                   wxT("rsgedit"),
                                   wxYES_NO|wxCANCEL|wxICON_QUESTION
                                   );

            switch (res)
                {
                default:
                    assert(false);
                    // fall through

                case wxCANCEL:
                    event.Veto();
                    return;

                case wxYES:
                    if (! SaveFile(edit))
                    {
                        event.Veto();
                        return;
                    }
                    break;

                case wxNO:
                    break;
                }
        }

    if (edit != 0)
        {
            SparkEdit::GetInstance().PutEdit(edit);
            UpdateTitle();
        }

    event.Skip();
}

void mainframe::OnTabStartScript(wxCommandEvent& event)
{
    wxScintilla* edit = SparkEdit::GetCurrentPage(mCtrNotebook);
    if (edit == 0)
    {
        event.Skip();
        return;
    }

    SparkEdit::GetInstance().SetStartScript(mCtrNotebook, edit);
}

void mainframe::OnTabCloseOther(wxCommandEvent& /*event*/)
{
    int sel = mCtrNotebook->GetSelection();
    if (sel < 0)
    {
        return;
    }

    int n = mCtrNotebook->GetPageCount();
    for (int i=(n-1);i>=0;--i)
    {
        if (i == sel)
        {
            continue;
        }

        bool notify = true;
        mCtrNotebook->RemovePage(i, notify);
    }
}

void mainframe::OnTabClose(wxCommandEvent& /*event*/)
{
    int sel = mCtrNotebook->GetSelection();
    if (sel < 0)
    {
        return;
    }

    bool notify = true;
    mCtrNotebook->RemovePage(sel, notify);
}

void mainframe::OnTabContextMenu(wxFlatNotebookEvent& event)
{
    wxScintilla* edit = SparkEdit::GetCurrentPage(mCtrNotebook);
    if (edit == 0)
    {
        event.Skip();
        return;
    }

    wxMenu* context = SparkContext::GetInstance().GetContextMenu(edit);
    if (context == 0)
        {
            return;
        }

    PopupMenu(context);
}

void mainframe::OnTabChanged(wxFlatNotebookEvent& event)
{
    wxWindow* page = mCtrNotebook->GetPage(event.GetSelection());
    if (page == mCanvas)
    {
        mCanvas->SetFocus();
    }

    event.Skip();
}

void mainframe::OnEditSavePointReached(wxScintillaEvent& event)
{
    SparkEdit::GetInstance().UpdateTitle(mCtrNotebook,static_cast<wxScintilla*>(event.GetEventObject()));
    event.Skip();
}

void mainframe::OnEditSavePointLeft(wxScintillaEvent& event)
{
    SparkEdit::GetInstance().UpdateTitle(mCtrNotebook,static_cast<wxScintilla*>(event.GetEventObject()));
    event.Skip();
}

void mainframe::OnEditCommand(wxCommandEvent& event)
{
    wxScintilla* edit = SparkEdit::GetCurrentPage(mCtrNotebook);
    if (edit == 0)
    {
        event.Skip();
        return;
    }

    switch (event.GetId())
        {
        case wxID_UNDO:
            edit->Undo();
            break;

        case wxID_REDO:
            edit->Redo();
            break;

        case wxID_CUT:
            edit->Cut ();
            break;

        case wxID_COPY:
            edit->Copy ();
            break;

        case wxID_PASTE:
            edit->Paste();
            break;

        case wxID_SELECTALL:
            edit->SetSelection (0, edit->GetLength());
            break;
        }
}

void mainframe::OnUpdateEditCommand(wxUpdateUIEvent& event)
{
    wxScintilla* edit = SparkEdit::GetCurrentPage(mCtrNotebook);
    if (edit == 0)
    {
        event.Enable(false);
        return;
    }

    switch (event.GetId())
        {
        case wxID_UNDO:
            {
                event.Enable(edit->CanUndo());
                break;
            }

        case wxID_REDO:
            {
                event.Enable(edit->CanRedo());
                break;

            }

        case wxID_CUT:
            {
                event.Enable(
                             (! edit->GetReadOnly()) &&
                             (edit->GetSelectionEnd()-edit->GetSelectionStart() > 0)
                             );
                break;
            }

        case wxID_COPY:
            {
                event.Enable
                    (
                     (edit->GetSelectionEnd()-edit->GetSelectionStart() > 0)
                     );
                break;
            }

        case wxID_PASTE:
            {
                event.Enable(edit->CanPaste());
                break;
            }

        case wxID_SELECTALL:
            {
                event.Enable(true);
                break;
            }
        }
}
